//!
//! An example for stateless functions.
//!

#[cfg(not(feature = "vs"))]
fn main() {}

#[cfg(feature = "vs")]
fn main() {
    real::run();
}

#[cfg(feature = "vs")]
mod real {

    use once_cell::sync::Lazy;
    use ruc::*;
    use serde::{Deserialize, Serialize};
    use std::{
        mem,
        sync::{mpsc::channel, Mutex},
        thread,
    };
    use vsdb::{
        BranchName, MapxVs, OrphanVs, ValueEnDe, VecxVs, VersionName, VersionNameOwned,
        Vs, VsMgmt,
    };

    type Amount = u64;
    type Address = Vec<u8>;
    type ConsensusInt = i128;

    #[derive(Vs, Clone, Debug, Serialize, Deserialize)]
    struct WorldState {
        transactions: VecxVs<Transaction>,
        balances: MapxVs<Address, Amount>,
        a_consensus_int: OrphanVs<ConsensusInt>,
    }

    const MASTER_BRANCH: &str = "master";
    const PRE_CHECK_BRANCH: &str = "pre_check";
    const FORMAL_CHECK_BRANCH: &str = "formal_check";

    static WORLD_STATE: Lazy<Mutex<WorldState>> =
        Lazy::new(|| Mutex::new(WorldState::load_or_init().unwrap()));

    static WORLD_STATE_SNAP_0: Lazy<Mutex<WorldState>> = Lazy::new(|| {
        let mut snap0 = WORLD_STATE.lock().unwrap().clone();
        pnk!(snap0.reset_branch(PRE_CHECK_BRANCH));
        Mutex::new(snap0)
    });

    static WORLD_STATE_SNAP_1: Lazy<Mutex<WorldState>> = Lazy::new(|| {
        let mut snap1 = WORLD_STATE.lock().unwrap().clone();
        pnk!(snap1.reset_branch(FORMAL_CHECK_BRANCH));
        Mutex::new(snap1)
    });

    static MEM_POOL: Lazy<Mutex<Vec<Transaction>>> = Lazy::new(|| Mutex::new(vec![]));

    fn random_version() -> VersionNameOwned {
        VersionNameOwned(
            (1_0000_0000 + rand::random::<u64>() / 2)
                .to_be_bytes()
                .to_vec(),
        )
    }

    fn transaction_pre_check(tx: Transaction) {
        let mut snap0 = WORLD_STATE_SNAP_0.lock().unwrap();
        snap0.push_version(&tx.hash()).unwrap();
        if snap0.apply_transaction(&tx).is_ok() {
            MEM_POOL.lock().unwrap().push(tx);
        } else {
            snap0.version_pop().unwrap();
        }
    }

    fn begin_block() {
        let mut snap0 = WORLD_STATE_SNAP_0.lock().unwrap();
        pnk!(snap0.reset_branch(PRE_CHECK_BRANCH));
        let mut snap1 = WORLD_STATE_SNAP_1.lock().unwrap();
        pnk!(snap1.reset_branch(FORMAL_CHECK_BRANCH));
    }

    fn transaction_formal_check_all() {
        let mut snap1 = WORLD_STATE_SNAP_1.lock().unwrap().clone();
        for tx in mem::take(&mut *MEM_POOL.lock().unwrap()).into_iter() {
            snap1.push_version(&tx.hash()).unwrap();
            if snap1.apply_transaction(&tx).is_err() {
                snap1.version_pop().unwrap();
            }
        }
    }

    fn end_block() {
        let mut snap1 = WORLD_STATE_SNAP_1.lock().unwrap();
        snap1.merge_branch(FORMAL_CHECK_BRANCH).unwrap();
    }

    impl WorldState {
        // sample code
        fn load_or_init() -> Result<Self> {
            let mut ws = WorldState {
                transactions: VecxVs::new(),
                balances: MapxVs::new(),
                a_consensus_int: OrphanVs::new(),
            };

            if !ws.branch_is_found(MASTER_BRANCH) {
                ws.push_version(b"init version").c(d!())?;
                ws.new_branch(MASTER_BRANCH).c(d!())?;
            }
            ws.set_default_branch(MASTER_BRANCH).c(d!())?;
            ws.push_version(b"init version 2").c(d!())?;

            ws.new_branch(PRE_CHECK_BRANCH).c(d!())?;
            ws.new_branch(FORMAL_CHECK_BRANCH).c(d!())?;

            Ok(ws)
        }

        fn apply_transaction(&mut self, tx: &Transaction) -> Result<()> {
            self.a_very_complex_function_will_change_state(tx).c(d!())
        }

        // sample code
        fn a_very_complex_function_will_change_state(
            &mut self,
            tx: &Transaction,
        ) -> Result<()> {
            if tx.from.get(0).is_some() {
                Ok(())
            } else {
                // ..........
                Err(eg!("error occur"))
            }
        }

        fn branch_is_found(&self, branch: &str) -> bool {
            let br = BranchName(branch.as_bytes());
            self.branch_exists(br)
        }

        fn new_branch(&mut self, branch: &str) -> Result<()> {
            let br = BranchName(branch.as_bytes());
            self.branch_create(br, random_version().as_deref(), false)
                .c(d!())
        }

        fn delete_branch(&mut self, branch: &str) -> Result<()> {
            let br = BranchName(branch.as_bytes());
            self.branch_remove(br).c(d!())
        }

        fn merge_branch(&mut self, branch: &str) -> Result<()> {
            let br = BranchName(branch.as_bytes());
            self.branch_merge_to(br, BranchName(MASTER_BRANCH.as_bytes()))
                .c(d!())
        }

        fn set_default_branch(&mut self, branch: &str) -> Result<()> {
            let br = BranchName(branch.as_bytes());
            self.branch_set_default(br).c(d!())
        }

        fn reset_branch(&mut self, branch: &str) -> Result<()> {
            self.set_default_branch(MASTER_BRANCH)
                .c(d!())
                .and_then(|_| self.delete_branch(branch).c(d!()))
                .and_then(|_| self.new_branch(branch).c(d!()))
                .and_then(|_| self.set_default_branch(branch).c(d!()))
        }

        fn push_version(&mut self, version: &[u8]) -> Result<()> {
            let ver = VersionName(version);
            self.version_create(ver).c(d!())
        }
    }

    #[derive(Default, Clone, Debug, Serialize, Deserialize)]
    struct Transaction {
        from: Address,
        to: Address,
        amount: Amount,
    }

    impl Transaction {
        fn hash(&self) -> Vec<u8> {
            // assume this is a hash function
            self.encode().to_vec()
        }
    }

    impl Transaction {
        fn new(amount: Amount) -> Self {
            Self {
                from: vec![],
                to: vec![],
                amount,
            }
        }
    }

    pub fn run() {
        let (sender, reveiver) = channel();

        thread::spawn(move || {
            loop {
                for tx in reveiver.iter() {
                    transaction_pre_check(tx);
                }
            }
        });

        (0..10).for_each(|i| sender.send(Transaction::new(i)).unwrap());

        sleep_ms!(60);

        begin_block();
        transaction_formal_check_all();
        end_block();
    }
}
